/*
  
  libc Wrapper for use by Fortran
  
  
  LICENSE AGREEMENT:
  
  You agree that we don't agree to anything.
  
  
  DISCLAIMER OF WARRANTIES:
  
  This software is provided to you "AS IS," and the authors and
  contributors disclaim any warranty or liability obligations to you
  of any kind, implied or express, including but not limited to the
  warranties of merchantability, fitness for a particular purpose,
  and non-infringement.
  
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
//
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <unistd.h>
//
#include <time.h>
#include <md5.h>
#include <sha.h>
#include <sha256.h>
//
#include <arpa/inet.h>
//
#include <sys/wait.h>
#include <signal.h>
//
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <fcntl.h>
#include <limits.h>


//
extern int * __error();
#define errno (* __error())

//
#define BUFSIZE 512
char ipstr[32];
int ip;
int port=3479;
int subnet;
void *termproc;

//
long int geterrnoc_(){
  return errno;
}

//
long int getipstr_(long int p){
  int len;
  len=strlen(ipstr);
  memcpy((char *)p,&ipstr,strlen(ipstr));
  return len;
}

//
void setsubnet_(int n){
  subnet=n;
  return;
}
//
void setport_(int n){
  port=n;
  return;
}
//
long int opentcp_(int port){ 
  int s, len;
  struct sockaddr_in  server;
  int optval=1;
  //
  if((s=socket(PF_INET,SOCK_STREAM,0))<0){
    perror("socket");
    return -1;
  }
  //
  if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,
		(char *)&optval,sizeof(optval))<0){
    perror("setsockopt");
    return -1;
  }
  //
  len=sizeof(server);
  memset((char *)&server, 0, len);
  server.sin_len = len;
  server.sin_family = PF_INET;
  server.sin_addr.s_addr  = htonl(INADDR_ANY);
  server.sin_port = htons(port);
  if(bind(s,(struct sockaddr *)&server,len)<0){
    perror("bind");
    return -1;
  }
  //
  return s;
}

//
long int listenaccept_(int s){
  int s2, len;
  int backlog=5;
  struct sockaddr_in client;
  //
  if(listen(s,backlog)<0){
    perror("listen");
    return -1;
  }
  memset((char *)&client, 0, sizeof(client));
  len = sizeof(client);
  if((s2=accept(s,(struct sockaddr *)&client,(socklen_t *)&len))<0){
    perror("accept");
    return -1;
  }
  ip=ntohl(client.sin_addr.s_addr);
  port=ntohs(client.sin_port);
  strcpy(ipstr,inet_ntoa(client.sin_addr));
  if((subnet&ip)!=ip){
    close(s2);
    fprintf(stderr,"accept: connection out of subnet\n");
    return -2;
  }else{
    return s2; 
  }
}
//
long int recv_(int s, char *str){
  int rc;
  rc=recv(s,str,BUFSIZE,0);
  if(rc<0) perror("recv");
  return rc;
}

//
void close_(int s){
  int rc;
  rc=close(s);
  if(rc!=0) perror("close");
  return;
}	      

//
void closetcp_(int s, int s2){
  int rc;
  rc=close(s2);
  if(rc!=0) perror("close");
  rc=close(s);
  if(rc!=0) perror("close");
  return;
}

//
long int recvdata_(int s, char *img, int len){
  int n,recvd;
  recvd=0;
  do {
    n=recv(s,img+recvd,BUFSIZE,0);
    if(n>0){
      recvd+=n;
    }else{
      if(n<0) perror("recvimg");
      return -1;
    }
  } while(recvd<len);
  return recvd;
}

//
long int send_(int s, char *str, int len){
  int rc;
  rc=send(s, str, len, 0);
  if(rc<0) perror("send");
  return rc;
}

// for debug
long int check_(char * img, int lenimg, char *result){
  int i;
  for(i=0;i<lenimg;i++){
    if(img[i]!=(char)(i%256)){
      printf("DATA ERROR at %d = %x\n",i,img[i]);
      strcpy(result,"data error detected");
      return -1;
    }
  }
  strcpy(result,"data check ok");
  return 0;
}

//
void c2fstr_(char *str, int len){
  int l;
  l=strlen(str);
  memset(str+l,32,len-l);
  return;
}

//
void md5data_(char *data, int len, char *res){
  res=MD5Data(data, len, res);
  return;
}
//
void sha1data_(char *data, int len, char *res){
  res=SHA1_Data(data,len,res);
  return;
}
//
void sha256data_(char *data, int len, char *res){
  res=SHA256_Data(data,len,res);
  return;
}
//
int waitpid_(int pid, int pstatus, int options){
  return waitpid(pid, 0, options);
}
//
void catch_SIGCHLD(int signo);
void catch_SIGTERM(int signo);
void setup_sigterm_(long int ptermproc)
{
  struct sigaction act;
  termproc=(void*)ptermproc; // <<< unused !
  memset(&act, 0, sizeof(act));
  act.sa_handler = catch_SIGTERM;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
  sigaction(SIGTERM, &act, NULL);
  sigaction(SIGHUP, &act, NULL); //<<<<<<<<<<<<<<<<<<<<<<<
}

void setup_sigchld_()
{
  struct sigaction act;
  memset(&act, 0, sizeof(act));
  act.sa_handler = catch_SIGCHLD;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
  sigaction(SIGCHLD, &act, NULL);
}
//
void catch_SIGCHLD(int signo)
{
  pid_t child_pid = 0;
  do {
    int child_ret;
    child_pid = waitpid(-1, &child_ret, WNOHANG);
    if(child_pid>0)
      printf("child %d exit with %d\n",child_pid,child_ret);
  } while(child_pid>0);
}
//
void catch_SIGTERM(int signo)
{
  printf("child pid=%d got term\n",getpid());
  void (*p_termproc)(void)=termproc;
  p_termproc();
  exit(-1); //<<<
  return;
}

//
long int timec_(void)
{
  return time(0);
}

//
long int isrunning_(long int pid)
{
  kvm_t *kd;
  int cnt;
  char errbuf[_POSIX2_LINE_MAX];
  struct kinfo_proc *kp;
  kd=kvm_open(NULL,"/dev/null",NULL,O_RDONLY, errbuf);
  if(kd==0){
    perror("kvm_open");
    return -1;
  }    
  kp=kvm_getprocs(kd,KERN_PROC_PID,pid,&cnt);
  if(kp==NULL) cnt=0;
  kvm_close(kd);
  return cnt;
}

long int cpu_(long int type)
{
  #define NCPUSTAT 5
  size_t len;
  long int cp_time[2][NCPUSTAT];
  long int total;
  long int ncpu;
  int i;
  char str[512];

  if(type==6||type>6){
    len=sizeof(long int);
    ncpu=0;
    if(sysctlbyname("kern.smp.cpus", &ncpu, &len, NULL, 0)) {
      perror("Couldn't retrieve CPU states");  
      return -1;
    }
    if(type==6)
      return ncpu;
  }

  total=0;
  bzero(&cp_time,sizeof(cp_time));
  len=sizeof(cp_time);
  if(sysctlbyname("kern.cp_time", &cp_time[0][0], &len, NULL, 0)) {
    perror("Couldn't retrieve CPU states");  
    return -1;
  }
  sleep(1);
  if(sysctlbyname("kern.cp_time", &cp_time[1][0], &len, NULL, 0)) {
    perror("Couldn't retrieve CPU states");  
    return -1;
  }
  //
  for(i=0;i<NCPUSTAT;i++){
    cp_time[0][i]=cp_time[1][i]-cp_time[0][i];
    total+=cp_time[0][i];
  }
  //
  for(i=0;i<NCPUSTAT;i++){
    printf("%f\n",(double)cp_time[0][i]/(double)total);
  }
  if(type==5){
    return 100.0*(double)(cp_time[0][0]+cp_time[0][1]+cp_time[0][2])/(double)total;
  }else if(type<5&&type>=0){
    return 100.0*(double)(cp_time[0][type])/(double)total;
  }else{
    /* type is a pointer to string */
    sprintf(str,"%ld/%f/%f/%f/%f/%f/%f",
	    ncpu,
	    (double)cp_time[0][0]/(double)total*100.,
	    (double)cp_time[0][1]/(double)total*100.,
	    (double)cp_time[0][2]/(double)total*100.,
	    (double)cp_time[0][3]/(double)total*100.,
	    (double)cp_time[0][4]/(double)total*100.,
	    (double)(cp_time[0][0]+cp_time[0][1]+cp_time[0][2])/(double)total*100.);
    len=strlen(str);
    memcpy((char *)type,&str,len);
    return len;
  }
}

long int f2b_(char *file, long int pbuf)
{
  FILE *f;
  long int size;

  f=fopen(file,"r");
  if(f==NULL){
    perror("fopen");
    return 0;
  }
  fseek(f,0,SEEK_END);
  size=ftell(f);
  fseek(f,0,SEEK_SET);
  //
  printf("file=%s pbuf=%ld\n",file,pbuf);
  if(fread((char*)pbuf,size,1,f)!=1){
    perror("fread");
    return 0;
  }
  //
  fclose(f);
  return size;
}
